package tl_check

import (
	"context"
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"

	//
	pb_stream "io.plus/test_loss/pkg/stream"

	"io.plus/test_loss/pkg/tl_model"
)

type ClientCheckArgs struct {
	// ensure
	Target string
	JobID  string

	// second
	JobDir      string
	JobFullPath string
	UseDB       bool
}

var GlbClientCheckArgs ClientCheckArgs

func TrimZeros(b []byte) []byte {
	var result []byte
	for _, value := range b {
		if value != 0 {
			result = append(result, value)
		}
	}
	return result
}

// 打开文件，读取文件名称，检查文件内容是否是 文件名称的 md5 值， 传入参数是 文件绝对路径， 返回值是 bool
func checkFileContentIsFileNameMD5(filePath string) bool {
	// 读取文件名
	fileName := filepath.Base(filePath)

	// 计算文件名的MD5值
	hash := md5.New()
	hash.Write([]byte(fileName))
	expectedMD5 := strings.TrimSpace(hex.EncodeToString(hash.Sum(nil)))

	// 打开文件并读取内容
	file, err := os.Open(filePath)
	if err != nil {
		log.Fatalf("could not open file: %v", err)
	}
	defer file.Close()

	content, err := io.ReadAll(file)
	if err != nil {
		log.Fatalf("could not read file: %v", err)
	}
	content = TrimZeros(content)

	// 比较文件内容和计算出的MD5值
	actualMD5 := strings.TrimSpace(string(content))
	// 将字节切片转换为字符串，并丢掉后面的零

	equal := strings.Compare(expectedMD5, actualMD5) == 0

	// 比较字符串是否相等
	// 去除字符串两边空白字符，并比较是否相等
	// strings.TrimSpace() 会去除字符串首尾空白字符
	return equal
}

// 利用 GlbClientCheckArgs.JobID 从服务器 请求获得 job max count
func CheckJobDirCorrect() {
	jobID := GlbClientCheckArgs.JobID

	check_args := GlbClientCheckArgs
	// 连接到gRPC服务器
	conn, err := grpc.NewClient(check_args.Target,
		grpc.WithTransportCredentials(insecure.NewCredentials()),
	)
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()

	// todo: x
	c := pb_stream.NewStreamServiceClient(conn)

	req := pb_stream.JobMaxCountRequest{Jobid: jobID}

	resp, err := c.JobMaxCount(context.Background(), &req)
	if err != nil {
		log.Fatalf("could not get job max count: %v", err)
	}
	max_count := resp.Maxcount

	err_info := resp.ErrInfo
	if err_info != "" {
		log.Fatalf("CheckJob err: %v", err_info)
	}

	if max_count == 0 {
		log.Fatalf("job max count is 0")
	}

	log.Printf("jobid:  %v", jobID)
	log.Printf("max count: %v", max_count)

	//
	checkJobRes := struct {
		Sum       int64
		Uncorrect int64
		Loss      int64
	}{
		Sum:       max_count,
		Uncorrect: 0,
		Loss:      0,
	}
	entrys, err := os.ReadDir(GlbClientCheckArgs.JobFullPath)
	if err != nil {
		log.Fatalf("os.ReadDir() failed: %v", err)
	}
	curcount := int64(0)

	if GlbClientCheckArgs.UseDB {
		// todo: x
		jon_info, err := tl_model.GetJobInfo(jobID)
		if err != nil {
			log.Fatalf("GetJobInfo() failed: %v", err)
		}
		curcount = jon_info.JobCount
	} else {
		// 遍历文件和子目录
		for _, file := range entrys {
			// 判断是否为文件, 并且是否全部都是自动生成的
			_, err := strconv.ParseInt(file.Name(), 10, 64)
			if err != nil {
				log.Fatalf("strconv.Atoi() failed: %v", err)
			}
			curcount += 1

			if !checkFileContentIsFileNameMD5(filepath.Join(GlbClientCheckArgs.JobFullPath, file.Name())) {
				checkJobRes.Uncorrect += 1
			}
		}
	}

	checkJobRes.Loss = max_count - curcount

	jsonData, err := json.Marshal(checkJobRes)
	if err != nil {
		fmt.Println("JSON Marshal fail:", err)
		return
	}

	log.Printf("checkJobRes: %v", string(jsonData))
}

func Run() {
	CheckJobDirCorrect()
}
